﻿#pragma once

#include <algorithm>
#include <deque>
#include <format>
#include <memory>
#include <set>
#include <stdexcept>
#include <unordered_set>
#include <vector>

#include <xy/topo/property.h>

namespace xy
{

namespace topo
{

// 前向声明
struct vertex_t;
struct edge_t;
struct coedge_t;
struct face_t;
struct wire_t;

// 句柄类型
using topo_prop = std::vector<std::shared_ptr<property_t>>;

// 拓扑方向
enum class Topo_Dir
{
    Forward,  // 与底层几何同向
    Backward, // 与底层几何反向
};

// 循环类型
enum class Topo_Loop
{
    Boundary, // 边界环
    Interior, // 内部环
};

class shape_t
{
  protected:
    /**
     * @brief 基本构造
     *
     */
    explicit shape_t() = default;

    /**
     * @brief 删除所有赋值和拷贝构造函数
     *
     */
    shape_t(shape_t &&) = delete;

    /**
     * @brief 析构函数
     *
     */
    virtual ~shape_t() = default;

  public:
    /**
     * @brief 添加属性
     *
     * @tparam T
     * @param[in] prop
     */
    template <typename T> void add_property(const property_handle<T> &prop)
    {
        // 注意创建一个新的属性，否则会有生命周期问题
        if (has_property(prop))
            throw std::runtime_error(std::format("Property already exists: {}", prop.name()));
        m_properties.emplace_back(std::make_shared<property_handle<T>>(prop.name()));
    }

    /**
     * @brief 属性操作
     *
     * @tparam T
     * @param[in] handle
     * @return const T&
     */
    template <typename T> const T &property(const property_handle<T> &handle) const
    {
        auto name = handle.name();
        auto it = std::find_if(m_properties.begin(), m_properties.end(),
                               [&](const auto &prop) { return prop->name() == name; });

        if (it == m_properties.end())
            throw std::runtime_error(std::format("Property not found: {}", name));

        // 因为创建的时候已经初始化了，所以一定能找到
        return std::static_pointer_cast<const property_handle<T>>(*it)->property();
    }

    /**
     * @brief 属性操作
     *
     * @tparam T
     * @param[in] handle
     * @return T&
     */
    template <typename T> T &property(const property_handle<T> &handle)
    {
        auto name = handle.name();
        auto it = std::find_if(m_properties.begin(), m_properties.end(),
                               [&](const auto &prop) { return prop->name() == name; });

        if (it == m_properties.end())
            throw std::runtime_error(std::format("Property not found: {}", name));

        // 因为创建的时候已经初始化了，所以一定能找到
        return std::static_pointer_cast<property_handle<T>>(*it)->property();
    }

    /**
     * @brief 是否具有属性
     *
     * @param[in] prop
     * @return true
     * @return false
     */
    bool has_property(const property_t &prop) const;

    /**
     * @brief 移除属性
     *
     * @param[in] prop
     */
    void remove_property(const property_t &prop);

    /**
     * @brief 删除对象
     *
     * @param[in] shape
     * @return true
     * @return false
     */
    static bool remove(shape_t *shape);

    /**
     * @brief 获得起始点
     *
     * @param[in] co
     * @return VertexHandle
     */
    static vertex_t *start_vertex(coedge_t *co);

    /**
     * @brief 获得目标点
     *
     * @param[in] co
     * @return vertex_t *
     */
    static vertex_t *end_vertex(coedge_t *co);

    /**
     * @brief 获得一个面上的所有 coedge_t（不包括其它 loop）（保顺序，逆时针）
     *
     * @param[in] f
     * @return std::vector<coedge_t *>
     */
    static std::vector<coedge_t *> coedges_on_face(face_t *f);

    /**
     * @brief 获取两个顶点的一条 coedge_t，从 v0 出发
     *
     * @param[in] v0
     * @param[in] v1
     * @return coedge_t *
     */
    static coedge_t *common_coedge_t(vertex_t *v0, vertex_t *v1);

    /**
     * @brief 获取两个面的公共边（保顺序，关于 f0 逆时针）
     *
     * @param[in] f0
     * @param[in] f1
     * @return std::vector<edge_t *>
     */
    static std::vector<edge_t *> common_edges(face_t *f0, face_t *f1);

    /**
     * @brief 获得离开 v 的所有 coedge_t（不保顺序）
     *
     * @param[in] v
     * @return std::vector<coedge_t *>
     */
    static std::vector<coedge_t *> adjacent_coedges(vertex_t *v);

    /**
     * @brief 获得 e 相邻的所有边（不保顺序）
     *
     * @param[in] e
     * @return std::unordered_set<edge_t *>
     */
    static std::unordered_set<edge_t *> adjacent_edges(edge_t *e);

    /**
     * @brief 获得 v 相邻的所有面（不保顺序）
     *
     * @param[in] v
     * @return std::unordered_set<face_t *>
     */
    static std::unordered_set<face_t *> adjacent_faces(vertex_t *v);

    /**
     * @brief 获得 f 相邻的所有面（不保顺序）
     *
     * @param[in] f
     * @return std::unordered_set<face_t *>
     */
    static std::unordered_set<face_t *> adjacent_faces(face_t *f);

    /**
     * @brief 获得 co 所在流形的所有边（保顺序，逆时针）
     *
     * @param[in] co
     * @return std::deque<edge_t *>
     */
    static std::deque<edge_t *> adjacent_ordered_edges(coedge_t *co);

    /**
     * @brief 获得 wire 上的所有 coedge_t（按照 wire 中 coedge_t 的连接顺序）
     *
     * @param[in] wire
     * @return std::vector<coedge_t *>
     */
    static std::vector<coedge_t *> edges(wire_t *wire);

  protected:
    topo_prop m_properties;
};

struct vertex_t : public shape_t
{
    // 相邻边
    std::vector<edge_t *> edges;

  protected:
    /**
     * @brief 空构造
     *
     */
    explicit vertex_t() = default;

    /**
     * @brief 删除所有赋值和拷贝构造函数
     *
     */
    vertex_t(vertex_t &&) = delete;

    /**
     * @brief 析构
     *
     */
    ~vertex_t() override;

  public:
    /**
     * @brief 创建顶点
     *
     * @return vertex_t *
     */
    static vertex_t *create();
};

struct edge_t : public shape_t
{
    coedge_t *coedge = nullptr; // 所属半边，此 coedge_t 与 edge_t 同向
    vertex_t *start = nullptr;  // 起点
    vertex_t *end = nullptr;    // 终点

  protected:
    /**
     * @brief 空构造
     *
     */
    explicit edge_t() = default;

    /**
     * @brief 删除所有赋值和拷贝构造函数
     *
     */
    edge_t(edge_t &&) = delete;

    /**
     * @brief 析构
     *
     */
    ~edge_t() override;

  public:
    /**
     * @brief 创建边
     *
     * @param[in] v1
     * @param[in] v2
     * @return edge_t *
     */
    static edge_t *create(vertex_t *v1, vertex_t *v2);
};

struct coedge_t : public shape_t
{
    coedge_t *twin = nullptr; // 反向边，一定存在
    coedge_t *prev = nullptr; // 上一条半边，一定存在，在面上
    coedge_t *next = nullptr; // 下一条半边，一定存在，在面上
    edge_t *edge = nullptr;   // 所属的边
    wire_t *wire = nullptr;   // 所属的 wire
    Topo_Dir dir;             // 拓扑方向

  protected:
    /**
     * @brief 方向构造
     *
     */
    explicit coedge_t(Topo_Dir dir);

    /**
     * @brief 删除所有赋值和拷贝构造函数
     *
     */
    coedge_t(coedge_t &&) = delete;

    /**
     * @brief 析构
     *
     */
    ~coedge_t() override;

  public:
    /**
     * @brief 创建半边
     *
     * @param[in] dir
     * @return coedge_t *
     */
    static coedge_t *create(Topo_Dir dir);

    /**
     * @brief 是否位于边界
     *
     * @return true
     * @return false
     */
    bool is_boundary() const;
};

// wire_t 的作用是构建 coedge_t 之间的连接关系
struct wire_t : public shape_t
{
    face_t *face = nullptr;               // 所属的 face_t
    coedge_t *coedge = nullptr;           // 包含的一条 coedge_t（首尾相连，所以不需要数组保存）
    Topo_Loop loop = Topo_Loop::Boundary; // 拓扑方向

  protected:
    /**
     * @brief 空构造
     *
     */
    explicit wire_t() = default;

    /**
     * @brief 删除所有赋值和拷贝构造函数
     *
     */
    wire_t(wire_t &&) = delete;

    /**
     * @brief 析构
     *
     */
    ~wire_t() override;

  public:
    /**
     * @brief 创建 wire
     *
     * @param[in] es
     * @return wire_t *
     */
    static wire_t *create(const std::vector<coedge_t *> &es);
};

struct face_t : public shape_t
{
    // 面上的所有 wire_t
    std::vector<wire_t *> wires;

  protected:
    /**
     * @brief 空构造
     *
     */
    explicit face_t() = default;

    /**
     * @brief 删除所有赋值和拷贝构造函数
     *
     */
    face_t(face_t &&) = delete;

    /**
     * @brief 析构
     *
     */
    ~face_t() override;

  public:
    /**
     * @brief 创建面
     *
     * @param[in] es
     * @return face_t *
     */
    static face_t *create(const std::vector<coedge_t *> &es);
};

} // namespace topo

} // namespace xy
